﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HydroDesktop.Interfaces;
using HydroDesktop.Interfaces.ObjectModel;
using HydroDesktop.Database;
using System.Data;
using System.IO;
using System.Globalization;
using System.Collections;
using HydroDesktop.ImportExport;
using System.ComponentModel;
using System.Threading;

namespace DroughtAnalysis
{
    public class VariableExportInfo
    {
        public VariableExportInfo(Variable var, string colName)
        {
            Variable = var;
            ColumnName = colName;
        }
        
        public Variable Variable { get; set; }
        public string ColumnName { get; set; }
    }
    
    /// <summary>
    /// Responsible for exporting data from stations to R text file
    /// </summary>
    public class DataExporter
    {
        private List<VariableExportInfo> _variableList = new List<VariableExportInfo>();

        private bool _createNewDirectoryForStation = true;
        
        /// <summary>
        /// List of variables to be exported for each station to the text file
        /// </summary>
        public IList<VariableExportInfo> VariablesToExport
        {
            get { return _variableList; }
        }

        /// <summary>
        /// Gets or sets if a new directory should be created for each station.
        /// If true, then the name of the directory will be same as the station name
        /// (without diacritics)
        /// </summary>
        public bool CreateNewDirectoryForStation 
        { 
            get { return _createNewDirectoryForStation; }
            set { _createNewDirectoryForStation = value; }     
        }

        /// <summary>
        /// Creates data files for all selected suitable stations
        /// </summary>
        /// <param name="stations">the selected suitable stations</param>
        /// <param name="startDate">Start date</param>
        /// <param name="endDate">End date</param>
        /// <param name="outputDirectory">the output directory</param>
        /// <param name="worker">The background worker object</param>
        /// <param name="e">E.Result returns RScriptGenerationResult</param>
        public void CreateInputFilesForR(IList<Site> stations, DateTime startDate, DateTime endDate, string outputDirectory, string scriptFileName,
            DroughtMethodInfo droughtMethod,
            BackgroundWorker worker, DoWorkEventArgs e)
        {
            var result = new RScriptGenerationResult { IsSuccessful = false };

            //todo use e.Error
            if (!Directory.Exists(outputDirectory))
            {
                throw new ArgumentException("Output Directory '" + outputDirectory + "' doesn't exist");
                //result.ErrorMessage = ("Output Directory '" + outputDirectory + "' doesn't exist");
                //e.Result = result;
                //return;
            }
            if (stations == null)
            {
                result.ErrorMessage = ("list of stations is not specified");
                e.Result = result;
                return;
            }
            
            List<DroughtSiteInfo> calculatedSiteList = new List<DroughtSiteInfo>();

            //for each site..
            for (int i=0; i< stations.Count; i++)
            {
                Site selectedStation = stations[i];
                
                //set the data file name
                string stationOutDir = outputDirectory;
                if (CreateNewDirectoryForStation)
                {
                    stationOutDir = StationNameHelper.PrepareStationDirectory(outputDirectory, selectedStation);
                }
                string dataFile = Path.Combine(stationOutDir, string.Format("{0}_data.dat", StationNameHelper.GetStationFileName(selectedStation.Name)));
                if (droughtMethod.InputFiles.Any())
                {
                    dataFile = Path.Combine(stationOutDir, droughtMethod.InputFiles[0]);
                }

                //report progress
                if (worker.WorkerReportsProgress)
                {
                    worker.ReportProgress((int)(i * 100 / stations.Count), String.Format("Creating data file: {0} ({1}/{2})", selectedStation.Name, i+1, stations.Count));
                }

                //generate data file
                ExportDataForStation(stations[i], startDate, endDate, dataFile, droughtMethod);

                //add result to list
                DroughtSiteInfo stationInfo = new DroughtSiteInfo { ResultDirectory = stationOutDir, Site = selectedStation };
                if (File.Exists(dataFile))
                {
                    stationInfo.CalculationStatus = RCalculationStatus.DataFileCreated;
                }
                else
                {
                    stationInfo.CalculationStatus = RCalculationStatus.None;
                }
                calculatedSiteList.Add(stationInfo);
            }

            //create the R-Script
            worker.ReportProgress(0, "Generating R-Script...");
            string combinedScriptFileName = Path.Combine(outputDirectory,"sucho_vse.R");
            RScriptModifier.CreateCombinedRScript(scriptFileName, combinedScriptFileName, calculatedSiteList, droughtMethod);

            result = new RScriptGenerationResult { RScriptFileName = combinedScriptFileName, DroughtStationList = calculatedSiteList };
            result.IsSuccessful = true;
            e.Result = result;
        }
        
        /// <summary>
        /// exports data for each station in the format: time, [variable1], [variable2]
        /// </summary>
        /// <param name="station">station</param>
        /// <param name="outFileName">out file name</param>
        public void ExportDataForStation(Site station, DateTime startDate, DateTime endDate, string outFileName, DroughtMethodInfo droughtMethod)
        {
            if (station.DataSeriesList == null)
                throw new ArgumentException("a list of DataSeries must be associated with the station");
            
            //valid variable ID's
            List<long> variableIDs = new List<long>();
            foreach(VariableExportInfo ve in _variableList)
            {
                variableIDs.Add(ve.Variable.Id);
            }
            
            //get the series

            var dvr = RepositoryFactory.Instance.Get<IDataValuesRepository>();
            //DataTable allValues = dvr.GetAll(

            var dbm = RepositoryFactory.Instance.Get < IRepositoryManager>(DatabaseTypes.SQLite, HydroDesktop.Configuration.Settings.Instance.DataRepositoryConnectionString);
      
            IList<Series> validSeriesList = station.DataSeriesList;

            DataGapInterpolator interp = new DataGapInterpolator();

            IList<CustomDataSeries> seriesListToExport = new List<CustomDataSeries>();
            foreach(Series dataSeries in validSeriesList)
            {
                //get the times and values
                IList<DateTime> times = dvr.GetTimesOrderByLocalDateTime(dataSeries.Id, startDate, endDate);
                IList<double> values = dvr.GetValuesOrderByLocalDateTime(dataSeries.Id, startDate, endDate);

                //do not include stations that don't have any data in the selected period
                if (times.Any() && values.Any())
                {
                    IList<TimeValuePair> tvp = interp.InterpolateDataGaps(times.ToArray(), values.ToArray(), 1, dataSeries.Variable.NoDataValue);
                    interp.ReplaceNoDataValues(tvp, dataSeries.Variable.NoDataValue);

                    CustomDataSeries cds = new CustomDataSeries();
                    cds.SeriesMetadata = dataSeries;
                    cds.Values = tvp;
                    seriesListToExport.Add(cds);
                }
            }

            //generate export table
            DataTable valuesTable = interp.SynchronizeSeries(seriesListToExport);

            //input variable names mapping

            
            //change the column names --> this should be redone.
            valuesTable.Columns[0].ColumnName = "Datum";
            for (int i = 0; i < station.DataSeriesList.Count; i++)
            {
                valuesTable.Columns[i+1].ColumnName = FindInputVariableColumnName(droughtMethod, station.DataSeriesList[i].Variable);
                
                //string vName = station.DataSeriesList[i].Variable.Name.ToLower();
                //if (vName.Contains("temp"))
                //{
                //    valuesTable.Columns[i + 1].ColumnName = droughtMethod.InputVariables[i].Speciation;
                //}
                //else if (vName.Contains("precip"))
                //{
                //    valuesTable.Columns[i + 1].ColumnName = droughtMethod.InputVariables[i

                //}
            }

            string separator = "\t";
            var formatOptions = new DelimitedFormatOptions { Append = false, Delimiter = separator, IncludeHeaders = true, UseShortDateFormat = true, UseInvariantCulture = true };

            Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
            using(StreamWriter wri = new StreamWriter(outFileName, false))
            {
                DelimitedTextWriter.DataTableToStream(valuesTable, wri, formatOptions);
            }
            //DelimitedTextWriter.DataTableToDelimitedFile(valuesTable, outFileName, separator, true);

        }

        private string FindInputVariableColumnName(DroughtMethodInfo droughtMethod, Variable seriesVariable)
        {
            string seriesVariableName = seriesVariable.Name.ToLower();
            foreach (Variable v in droughtMethod.InputVariables)
            {
                if (v.Name.ToLower() == seriesVariableName)
                {
                    return v.Speciation;
                }
            }
            return seriesVariableName;
        }

        private string FindOutputVariableColumnName(DroughtMethodInfo droughtMethod, Variable seriesVariable)
        {
            return seriesVariable.Name;
        }

        
    }
}
